/*
 * Copyright 2021 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openrewrite.maven;

import com.google.common.collect.Lists;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.maven.tree.MavenResolutionResult;
import org.openrewrite.test.RewriteTest;
import org.openrewrite.test.SourceSpec;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openrewrite.java.Assertions.mavenProject;
import static org.openrewrite.maven.Assertions.pomXml;

class UpgradeDependencyVersionTest implements RewriteTest {

    @DocumentExample
    @Test
    void updateManagedDependencyVersion() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.junit.jupiter", "junit-jupiter-api", "5.7.2", null,
            null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.junit.jupiter</groupId>
                              <artifactId>junit-jupiter-api</artifactId>
                              <version>5.6.2</version>
                              <scope>test</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.junit.jupiter</groupId>
                              <artifactId>junit-jupiter-api</artifactId>
                              <version>5.7.2</version>
                              <scope>test</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void forceUpgradeNonSemverVersion() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.springframework.cloud", "spring-cloud-dependencies", "2022.0.2", null,
            false, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.springframework.cloud</groupId>
                              <artifactId>spring-cloud-dependencies</artifactId>
                              <version>Camden.SR5</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.springframework.cloud</groupId>
                              <artifactId>spring-cloud-dependencies</artifactId>
                              <version>2022.0.2</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @CsvSource(value = {"com.google.guava:guava", "*:*"}, delimiter = ':')
    @ParameterizedTest
    void upgradeVersion(String groupId, String artifactId) {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion(groupId, artifactId, "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>13.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>13.0.1</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void overrideManagedDependency() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "guava", "14.0", "", true, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-parent</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>com.google.guava</groupId>
                              <artifactId>guava</artifactId>
                              <version>13.0</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            SourceSpec::skip
          ),
          mavenProject("my-child",
            pomXml(
              """
                <project>
                    <parent>
                        <groupId>com.mycompany</groupId>
                        <artifactId>my-parent</artifactId>
                        <version>1</version>
                    </parent>
                    <groupId>com.mycompany</groupId>
                    <artifactId>my-child</artifactId>
                    <version>1</version>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                        </dependency>
                    </dependencies>
                </project>
                """,
              """
                    <project>
                        <parent>
                            <groupId>com.mycompany</groupId>
                            <artifactId>my-parent</artifactId>
                            <version>1</version>
                        </parent>
                        <groupId>com.mycompany</groupId>
                        <artifactId>my-child</artifactId>
                        <version>1</version>
                        <dependencies>
                            <dependency>
                                <groupId>com.google.guava</groupId>
                                <artifactId>guava</artifactId>
                                <version>14.0</version>
                            </dependency>
                        </dependencies>
                    </project>
                """
            )
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/739")
    @Test
    void upgradeVersionWithGroupIdAndArtifactIdDefinedAsProperty() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("io.quarkus", "quarkus-universe-bom", "1.13.7.Final", null,
            null, null)),
          pomXml(
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
                      <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
                      <quarkus.platform.version>1.11.7.Final</quarkus.platform.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>${quarkus.platform.group-id}</groupId>
                              <artifactId>${quarkus.platform.artifact-id}</artifactId>
                              <version>${quarkus.platform.version}</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
                      <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
                      <quarkus.platform.version>1.13.7.Final</quarkus.platform.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>${quarkus.platform.group-id}</groupId>
                              <artifactId>${quarkus.platform.artifact-id}</artifactId>
                              <version>${quarkus.platform.version}</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeVersionSuccessively() {
        rewriteRun(
          spec -> spec.recipes(
            new UpgradeDependencyVersion("com.google.guava", "*", "28.x", "-jre", null, null),
            new UpgradeDependencyVersion("com.google.guava", "*", "29.x", "-jre", null, null)
          ),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>27.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>29.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeVersionToRelease() {
        rewriteRun(
          spec -> spec.recipe(
            new UpgradeDependencyVersion("org.springframework.boot", "spring-boot", "3.5.x", null, null, null)
          ),
          //language=xml
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <properties>
                  <spring-boot.version>3.1.5</spring-boot.version>
                </properties>
                <dependencies>
                  <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot</artifactId>
                    <version>${spring-boot.version}</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            spec -> spec.after(actual ->
              assertThat(actual)
                .containsPattern("<spring-boot.version>3.5.\\d+</spring-boot.version>")
                .actual()
            )
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/565")
    @Test
    void propertiesInDependencyGroupIdAndArtifactId() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <dependency.group-id>com.google.guava</dependency.group-id>
                      <dependency.artifact-id>guava</dependency.artifact-id>
                      <dependency.version>13.0</dependency.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>${dependency.group-id}</groupId>
                          <artifactId>${dependency.artifact-id}</artifactId>
                          <version>${dependency.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <dependency.group-id>com.google.guava</dependency.group-id>
                      <dependency.artifact-id>guava</dependency.artifact-id>
                      <dependency.version>13.0.1</dependency.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>${dependency.group-id}</groupId>
                          <artifactId>${dependency.artifact-id}</artifactId>
                          <version>${dependency.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void retainReleaseOrLatestIfUsed() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.projectlombok", "lombok", "1.18.*", null, null, null)),
          //language=xml
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>RELEASE</version>
                  </dependency>
                  <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>LATEST</version>
                    <scope>test</scope>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeGuava() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "*", "25-28", "-android", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>25.0-android</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>28.0-android</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void upgradePluginDependencies() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.openrewrite.recipe", "rewrite-spring", "5.0.6", "", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.openrewrite.maven</groupId>
                      <artifactId>rewrite-maven-plugin</artifactId>
                      <version>5.4.1</version>
                      <dependencies>
                        <dependency>
                          <groupId>org.openrewrite.recipe</groupId>
                          <artifactId>rewrite-spring</artifactId>
                          <version>5.0.5</version>
                        </dependency>
                      </dependencies>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.openrewrite.maven</groupId>
                      <artifactId>rewrite-maven-plugin</artifactId>
                      <version>5.4.1</version>
                      <dependencies>
                        <dependency>
                          <groupId>org.openrewrite.recipe</groupId>
                          <artifactId>rewrite-spring</artifactId>
                          <version>5.0.6</version>
                        </dependency>
                      </dependencies>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeAnnotationProcessors() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.mapstruct", "mapstruct*", "1.6.x", null, null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                    <dependency>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct</artifactId>
                        <version>1.4.1.Final</version>
                    </dependency>
                </dependencies>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <version>3.8.1</version>
                      <configuration>
                        <annotationProcessorPaths>
                          <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>1.4.1.Final</version>
                          </path>
                        </annotationProcessorPaths>
                      </configuration>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """,
            spec -> spec.after(actual ->
              assertThat(actual)
                .doesNotContain("1.4.1.Final")
                .containsPattern("<version>1.6.\\d+(.\\d+)?</version>")
                .actual())
          )
        );
    }

    @Test
    void upgradeAnnotationProcessorsVersionProperty() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.mapstruct", "mapstruct*", "1.6.x", null, null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <properties>
                    <org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
                </properties>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <version>3.8.1</version>
                      <configuration>
                        <annotationProcessorPaths>
                          <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                          </path>
                        </annotationProcessorPaths>
                      </configuration>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """,
            spec -> spec.after(actual ->
              assertThat(actual)
                .doesNotContain("1.4.1.Final")
                .containsPattern("<org.mapstruct.version>1.6.\\d+(.\\d+)?</org.mapstruct.version>")
                .actual())
          )
        );
    }

    @Test
    void upgradePluginDependenciesOnProperty() {
        rewriteRun(
          // Using latest.patch to validate the version property resolution, since it's needed for matching the valid patch.
          spec -> spec.recipe(new UpgradeDependencyVersion("org.openrewrite.recipe", "rewrite-spring", "latest.patch", "", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <properties>
                    <rewrite-spring.version>4.33.0</rewrite-spring.version>
                </properties>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.openrewrite.maven</groupId>
                      <artifactId>rewrite-maven-plugin</artifactId>
                      <version>5.4.1</version>
                      <dependencies>
                        <dependency>
                          <groupId>org.openrewrite.recipe</groupId>
                          <artifactId>rewrite-spring</artifactId>
                          <version>${rewrite-spring.version}</version>
                        </dependency>
                      </dependencies>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <properties>
                    <rewrite-spring.version>4.33.2</rewrite-spring.version>
                </properties>
                <build>
                  <plugins>
                    <plugin>
                      <groupId>org.openrewrite.maven</groupId>
                      <artifactId>rewrite-maven-plugin</artifactId>
                      <version>5.4.1</version>
                      <dependencies>
                        <dependency>
                          <groupId>org.openrewrite.recipe</groupId>
                          <artifactId>rewrite-spring</artifactId>
                          <version>${rewrite-spring.version}</version>
                        </dependency>
                      </dependencies>
                    </plugin>
                  </plugins>
                </build>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/1334")
    @Test
    void upgradeGuavaWithExplicitBlankVersionPattern() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "*", "latest.release", "", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>22.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>23.0</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeManagedInParent() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "*", "25-28", "-jre", false, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <guava.version>25.0-jre</guava.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>com.google.guava</groupId>
                              <artifactId>guava</artifactId>
                              <version>${guava.version}</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <guava.version>28.0-jre</guava.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>com.google.guava</groupId>
                              <artifactId>guava</artifactId>
                              <version>${guava.version}</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          ),
          mavenProject("my-app-server",
            pomXml(
              """
                    <project>
                        <parent>
                            <groupId>com.mycompany.app</groupId>
                            <artifactId>my-app</artifactId>
                            <version>1</version>
                        </parent>
                        <groupId>com.mycompany.app</groupId>
                        <artifactId>my-app-server</artifactId>
                        <version>1</version>
                        <dependencies>
                            <dependency>
                                <groupId>com.google.guava</groupId>
                                <artifactId>guava</artifactId>
                            </dependency>
                        </dependencies>
                    </project>
                """
            )
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/891")
    @Test
    void upgradeDependencyOnlyTargetsSpecificDependencyProperty() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "*", "25-28", "-jre", null, null)),
          pomXml(
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <guava.version>25.0-jre</guava.version>
                      <spring.version>5.3.9</spring.version>
                      <spring.artifact-id>spring-jdbc</spring.artifact-id>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>com.google.guava</groupId>
                          <artifactId>guava</artifactId>
                          <version>${guava.version}</version>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework</groupId>
                          <artifactId>${spring.artifact-id}</artifactId>
                          <version>${spring.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <properties>
                      <guava.version>28.0-jre</guava.version>
                      <spring.version>5.3.9</spring.version>
                      <spring.artifact-id>spring-jdbc</spring.artifact-id>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>com.google.guava</groupId>
                          <artifactId>guava</artifactId>
                          <version>${guava.version}</version>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework</groupId>
                          <artifactId>${spring.artifact-id}</artifactId>
                          <version>${spring.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4193")
    @Test
    void upgradeVersionDefinedViaPropertyInLocalParent() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "guava", "14.0", "", false, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-parent</artifactId>
                  <version>1</version>
                  <properties>
                    <guava.version>13.0</guava.version>
                    <junit.version>4.13.2</junit.version>
                  </properties>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-parent</artifactId>
                  <version>1</version>
                  <properties>
                    <guava.version>14.0</guava.version>
                    <junit.version>4.13.2</junit.version>
                  </properties>
              </project>
              """
          ),
          mavenProject("my-child",
            pomXml(
              """
                <project>
                    <parent>
                        <groupId>com.mycompany</groupId>
                        <artifactId>my-parent</artifactId>
                        <version>1</version>
                    </parent>
                    <groupId>com.mycompany</groupId>
                    <artifactId>my-child</artifactId>
                    <version>1</version>
                    <dependencies>
                        <dependency>
                            <groupId>com.google.guava</groupId>
                            <artifactId>guava</artifactId>
                            <version>${guava.version}</version>
                        </dependency>
                        <dependency>
                            <groupId>junit</groupId>
                            <artifactId>junit</artifactId>
                            <version>${junit.version}</version>
                            <scope>test</scope>
                        </dependency>
                    </dependencies>
                </project>
                """
            )
          )
        );
    }

    @Test
    void upgradeVersionDefinedViaImplicitPropertyInDependencyManagementBom() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.flywaydb", "flyway-core", "10.15.0", "", true, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                      </dependency>
                  </dependencies>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.springframework.boot</groupId>
                              <artifactId>spring-boot-dependencies</artifactId>
                              <version>3.3.0</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                          <version>10.15.0</version>
                      </dependency>
                  </dependencies>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.springframework.boot</groupId>
                              <artifactId>spring-boot-dependencies</artifactId>
                              <version>3.3.0</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }


    @Test
    void upgradeVersionDefinedViaExplicitPropertyInDependencyManagement() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.projectlombok", "lombok", "1.18.30", "", true, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <properties>
                      <lombok.version>1.18.22</lombok.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok</artifactId>
                      </dependency>
                  </dependencies>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok</artifactId>
                          <version>${lombok.version}</version>
                      </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <properties>
                      <lombok.version>1.18.30</lombok.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok</artifactId>
                      </dependency>
                  </dependencies>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok</artifactId>
                          <version>${lombok.version}</version>
                      </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4193")
    @Test
    void upgradeVersionDefinedViaImplicitPropertyInRemoteParent() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.flywaydb", "flyway-core", "10.15.0", "", true, null)),
          pomXml(
            """
              <project>
                  <parent>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>3.3.0</version>
                  </parent>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <parent>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>3.3.0</version>
                  </parent>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <properties>
                      <flyway.version>10.15.0</flyway.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4193")
    @Test
    void upgradeVersionDefinedViaExplicitPropertyInRemoteParent() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.flywaydb", "flyway-core", "10.15.0", "", true, null)),
          pomXml(
            """
              <project>
                  <parent>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>3.3.0</version>
                  </parent>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                          <version>${flyway.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <parent>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>3.3.0</version>
                  </parent>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-child</artifactId>
                  <version>1</version>
                  <properties>
                      <flyway.version>10.15.0</flyway.version>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>org.flywaydb</groupId>
                          <artifactId>flyway-core</artifactId>
                          <version>${flyway.version}</version>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4193")
    @Test
    void upgradeVersionDefinedViaNestedExplicitPropertyInRemoteParent() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.flywaydb", "flyway-core", "10.15.0", "", true, null)),
          pomXml(
            """
              <project>
                  <parent>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>3.3.0</version>
                  </parent>
                  <groupId>com.mycompany</groupId>
                  <artifactId>my-parent</artifactId>
                  <version>1</version>
              </project>
              """
          ),
          mavenProject("my-child",
            pomXml(
              """
                <project>
                    <parent>
                        <groupId>com.mycompany</groupId>
                        <artifactId>my-parent</artifactId>
                        <version>1</version>
                    </parent>
                    <groupId>com.mycompany</groupId>
                    <artifactId>my-child</artifactId>
                    <version>1</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.flywaydb</groupId>
                            <artifactId>flyway-core</artifactId>
                            <version>${flyway.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """,
              """
                <project>
                    <parent>
                        <groupId>com.mycompany</groupId>
                        <artifactId>my-parent</artifactId>
                        <version>1</version>
                    </parent>
                    <groupId>com.mycompany</groupId>
                    <artifactId>my-child</artifactId>
                    <version>1</version>
                    <properties>
                        <flyway.version>10.15.0</flyway.version>
                    </properties>
                    <dependencies>
                        <dependency>
                            <groupId>org.flywaydb</groupId>
                            <artifactId>flyway-core</artifactId>
                            <version>${flyway.version}</version>
                        </dependency>
                    </dependencies>
                </project>
                """
            )
          )
        );
    }

    @Test
    void upgradeBomImport() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("io.micronaut", "micronaut-bom", "3.0.0-M5", null, null, null)),
          pomXml(
            """
              <project>
                <groupId>org.openrewrite.example</groupId>
                <artifactId>my-app-server</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>io.micronaut</groupId>
                      <artifactId>micronaut-bom</artifactId>
                      <version>2.5.10</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
              </project>
              """,
            """
              <project>
                <groupId>org.openrewrite.example</groupId>
                <artifactId>my-app-server</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>io.micronaut</groupId>
                      <artifactId>micronaut-bom</artifactId>
                      <version>3.0.0-M5</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeAllManagedDependenciesToPatchReleases() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("*", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app-server</artifactId>
                  <version>1</version>
                  <properties>
                      <micronaut.version>2.5.10</micronaut.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>io.micronaut</groupId>
                              <artifactId>micronaut-bom</artifactId>
                              <version>${micronaut.version}</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                          <dependency>
                              <groupId>javax.servlet</groupId>
                              <artifactId>javax.servlet-api</artifactId>
                              <version>4.0.0</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app-server</artifactId>
                  <version>1</version>
                  <properties>
                      <micronaut.version>2.5.13</micronaut.version>
                  </properties>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>io.micronaut</groupId>
                              <artifactId>micronaut-bom</artifactId>
                              <version>${micronaut.version}</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                          <dependency>
                              <groupId>javax.servlet</groupId>
                              <artifactId>javax.servlet-api</artifactId>
                              <version>4.0.1</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeAllDependenciesToPatchReleases() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("*", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app-server</artifactId>
                  <version>1</version>
                  <properties>
                      <guava.version>28.0-jre</guava.version>
                      <spring.version>5.3.4</spring.version>
                      <spring.artifact-id>spring-jdbc</spring.artifact-id>
                  </properties>
                  <dependencies>
                      <dependency>
                          <groupId>com.google.guava</groupId>
                          <artifactId>guava</artifactId>
                          <version>${guava.version}</version>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework</groupId>
                          <artifactId>${spring.artifact-id}</artifactId>
                          <version>${spring.version}</version>
                      </dependency>
                      <dependency>
                          <groupId>com.fasterxml.jackson.core</groupId>
                          <artifactId>jackson-core</artifactId>
                          <version>2.13.1</version>
                      </dependency>
                  </dependencies>
              </project>
              """,
            spec -> spec.after(after -> {
                Matcher matcher = Pattern.compile("<spring\\.version>(.+)</spring\\.version>").matcher(after);
                assertTrue(matcher.find());
                String springVersion = matcher.group(1);
                assertNotEquals("5.3.4", springVersion);
                return """
                  <project>
                      <groupId>org.openrewrite.example</groupId>
                      <artifactId>my-app-server</artifactId>
                      <version>1</version>
                      <properties>
                          <guava.version>28.0-jre</guava.version>
                          <spring.version>%s</spring.version>
                          <spring.artifact-id>spring-jdbc</spring.artifact-id>
                      </properties>
                      <dependencies>
                          <dependency>
                              <groupId>com.google.guava</groupId>
                              <artifactId>guava</artifactId>
                              <version>${guava.version}</version>
                          </dependency>
                          <dependency>
                              <groupId>org.springframework</groupId>
                              <artifactId>${spring.artifact-id}</artifactId>
                              <version>${spring.version}</version>
                          </dependency>
                          <dependency>
                              <groupId>com.fasterxml.jackson.core</groupId>
                              <artifactId>jackson-core</artifactId>
                              <version>2.13.5</version>
                          </dependency>
                      </dependencies>
                  </project>
                  """.formatted(springVersion);
            })
          )
        );
    }

    @Test
    void dependencyManagementResolvedFromProperty() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("io.micronaut", "micronaut-bom", "3.0.0-M5", null, null, null)),
          pomXml(
            """
              <project>
                <groupId>org.openrewrite</groupId>
                <artifactId>my-app-server</artifactId>
                <version>1.0.0</version>
                <properties>
                  <micronaut.version>2.5.11</micronaut.version>
                </properties>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>io.micronaut</groupId>
                      <artifactId>micronaut-bom</artifactId>
                      <version>${micronaut.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
              </project>
              """,
            """
              <project>
                <groupId>org.openrewrite</groupId>
                <artifactId>my-app-server</artifactId>
                <version>1.0.0</version>
                <properties>
                  <micronaut.version>3.0.0-M5</micronaut.version>
                </properties>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>io.micronaut</groupId>
                      <artifactId>micronaut-bom</artifactId>
                      <version>${micronaut.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4166")
    @ParameterizedTest
    @ValueSource(strings = {"3.0.12.RELEASE", "=3.0.12.RELEASE"})
    void upgradeToExactVersion(String version) {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.thymeleaf", "thymeleaf-spring5", version, null,
            null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.thymeleaf</groupId>
                          <artifactId>thymeleaf-spring5</artifactId>
                          <version>3.0.8.RELEASE</version>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.thymeleaf</groupId>
                          <artifactId>thymeleaf-spring5</artifactId>
                          <version>3.0.12.RELEASE</version>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/1248")
    @Test
    void updateWithExactVersionRange() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("junit", "junit", "4.13", null, false, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>[4.11]</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.13</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void deriveFromNexus() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("*", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>net.sourceforge.saxon</groupId>
                              <artifactId>saxon</artifactId>
                              <version>9.1.0.8</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void deriveFromNexusUpgrade() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("*", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>net.sourceforge.orbroker</groupId>
                              <artifactId>orbroker</artifactId>
                              <version>2.0.3</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>net.sourceforge.orbroker</groupId>
                              <artifactId>orbroker</artifactId>
                              <version>2.0.4</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void noManagedVersion() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("*", "*", "latest.patch", null, null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>net.sourceforge.orbroker</groupId>
                              <artifactId>orbroker</artifactId>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void removesRedundantExplicitVersionsMatchingOldImport() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.junit", "junit-bom", "5.9.1", null, true, null)),
          pomXml(
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.0</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                    <version>5.9.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.1</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void removesRedundantExplicitVersionsMatchingNewImport() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.junit", "junit-bom", "5.9.1", null, true, null)),
          pomXml(
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.0</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                    <version>5.9.1</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.1</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.junit", "junit-bom", "5.9.1", null, true, null)),
          pomXml(
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.0</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                    <version>5.8.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <modelVersion>4.0.0</modelVersion>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.junit</groupId>
                      <artifactId>junit-bom</artifactId>
                      <version>5.9.1</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                    <version>5.8.0</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/2418")
    @Nested
    class RetainVersions {
        @DocumentExample
        @Test
        void dependencyWithExplicitVersionRemovedFromDepMgmt() {
            rewriteRun(spec -> spec.recipe(new UpgradeDependencyVersion("org.springframework.cloud", "spring-cloud-config-dependencies", "3.1.4", null, true, singletonList("com.jcraft:jsch"))),
              pomXml(
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.2</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.55</version>
                      </dependency>
                    </dependencies>
                  </project>
                  """,
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.4</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.55</version>
                      </dependency>
                    </dependencies>
                  </project>
                  """
              )
            );
        }

        @Test
        void dependencyWithoutExplicitVersionRemovedFromDepMgmt() {
            rewriteRun(spec -> spec.recipe(new UpgradeDependencyVersion("org.springframework.cloud", "spring-cloud-config-dependencies", "3.1.4", null, true, singletonList("com.jcraft:jsch"))),
              pomXml(
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.2</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                      </dependency>
                    </dependencies>
                  </project>
                  """,
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.4</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.55</version>
                      </dependency>
                    </dependencies>
                  </project>
                  """
              )
            );
        }

        @Test
        void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() {
            rewriteRun(spec -> spec.recipe(new UpgradeDependencyVersion("org.springframework.cloud", "spring-cloud-config-dependencies", "3.1.4", null, true, singletonList("com.jcraft:jsch:0.1.50"))),
              pomXml(
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.2</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                      </dependency>
                    </dependencies>
                  </project>
                  """,
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-config-dependencies</artifactId>
                          <version>3.1.4</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.50</version>
                      </dependency>
                    </dependencies>
                  </project>
                  """
              )
            );
        }

        @Test
        void multipleRetainVersions() {
            rewriteRun(spec -> spec.recipe(
                new UpgradeDependencyVersion("org.springframework.cloud", "spring-cloud-dependencies", "2021.0.5", null,
                  true,
                  Lists.newArrayList("com.jcraft:jsch", "org.springframework.cloud:spring-cloud-schema-registry-*:1.1.1"))),
              pomXml(
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-dependencies</artifactId>
                          <version>2020.0.1</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                      </dependency>
                      <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-schema-registry-core</artifactId>
                      </dependency>
                    </dependencies>
                  </project>
                  """,
                """
                  <project>
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>org.sample</groupId>
                    <artifactId>sample</artifactId>
                    <version>1.0.0</version>
                    <dependencyManagement>
                      <dependencies>
                        <dependency>
                          <groupId>org.springframework.cloud</groupId>
                          <artifactId>spring-cloud-dependencies</artifactId>
                          <version>2021.0.5</version>
                          <type>pom</type>
                          <scope>import</scope>
                        </dependency>
                      </dependencies>
                    </dependencyManagement>
                    <dependencies>
                      <dependency>
                        <groupId>com.jcraft</groupId>
                        <artifactId>jsch</artifactId>
                        <version>0.1.55</version>
                      </dependency>
                      <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-schema-registry-core</artifactId>
                        <version>1.1.1</version>
                      </dependency>
                    </dependencies>
                  </project>
                  """
              )
            );
        }
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/4333")
    @Test
    void exactVersionWithPattern() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "guava", "29.0", "-jre", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>25.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>29.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void mavenUsesLatestMentionedArtifactForResolution() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.google.guava", "guava", "29.0", "-jre", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>25.0-jre</version>
                  </dependency>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>23.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>25.0-jre</version>
                  </dependency>
                  <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>29.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Disabled("Service Unavailable")
    @Test
    void exactVersionMissingInMavenMetadata() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.apache.hbase", "hbase-annotations", "1.4.14.1-mapr-640",
            null, null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <repositories>
                    <repository>
                        <id>hbase</id>
                        <url>https://repository.mapr.com/nexus/content/groups/mapr-public</url>
                        <snapshots>
                            <enabled>false</enabled>
                        </snapshots>
                    </repository>
                </repositories>
                <dependencies>
                  <dependency>
                    <groupId>org.apache.hbase</groupId>
                    <artifactId>hbase-annotations</artifactId>
                    <version>1.1.1-mapr-1602</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <repositories>
                    <repository>
                        <id>hbase</id>
                        <url>https://repository.mapr.com/nexus/content/groups/mapr-public</url>
                        <snapshots>
                            <enabled>false</enabled>
                        </snapshots>
                    </repository>
                </repositories>
                <dependencies>
                  <dependency>
                    <groupId>org.apache.hbase</groupId>
                    <artifactId>hbase-annotations</artifactId>
                    <version>1.4.14.1-mapr-640</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeVersionWithGroupIdAndArtifactIdDefinedAsMavenConfigProperty() {
        rewriteRun(
          spec -> spec
            .parser(MavenParser.builder()
              .property("quarkus.platform.group-id", "io.quarkus")
              .property("quarkus.platform.artifact-id", "quarkus-bom")
              .property("quarkus.platform.version", "1.11.7.Final"))
            .recipe(new UpgradeDependencyVersion("io.quarkus", "quarkus-universe-bom", "1.13.7.Final", null,
              null, null)),
          pomXml(
            """
              <project>
                  <groupId>org.openrewrite.example</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>${quarkus.platform.group-id}</groupId>
                              <artifactId>${quarkus.platform.artifact-id}</artifactId>
                              <version>${quarkus.platform.version}</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """,
            spec -> spec.afterRecipe(p -> {
                  var results = p.getMarkers().findFirst(MavenResolutionResult.class).orElseThrow();
                  // `maven.config` properties are read-only at the moment, so version stays te same
                  assertThat(results.getPom().getProperties().get("quarkus.platform.version")).isEqualTo("1.11.7.Final");
              }
            )
          )
        );
    }

    @Test
    void doesNotDowngradeRegularDependencyVersion() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.apache.tomcat.embed", "tomcat-embed-core", "10.1.33", null,
            null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencies>
                      <dependency>
                          <groupId>org.apache.tomcat.embed</groupId>
                          <artifactId>tomcat-embed-core</artifactId>
                          <version>10.1.43</version>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void doesNotDowngradeManagedDependencyVersion() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.apache.tomcat.embed", "tomcat-embed-core", "10.1.33", null,
            null, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>org.apache.tomcat.embed</groupId>
                              <artifactId>tomcat-embed-core</artifactId>
                              <version>10.1.43</version>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void upgradeBomInsteadOfOverridingDependency() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("com.fasterxml.jackson.core", "jackson-core", "2.15.0", null,
            true, null)),
          pomXml(
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>com.fasterxml.jackson</groupId>
                              <artifactId>jackson-bom</artifactId>
                              <version>2.14.2</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
                  <dependencies>
                      <dependency>
                          <groupId>com.fasterxml.jackson.core</groupId>
                          <artifactId>jackson-core</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>com.fasterxml.jackson.core</groupId>
                          <artifactId>jackson-databind</artifactId>
                      </dependency>
                  </dependencies>
              </project>
              """,
            """
              <project>
                  <groupId>com.mycompany.app</groupId>
                  <artifactId>my-app</artifactId>
                  <version>1</version>
                  <dependencyManagement>
                      <dependencies>
                          <dependency>
                              <groupId>com.fasterxml.jackson</groupId>
                              <artifactId>jackson-bom</artifactId>
                              <version>2.15.0</version>
                              <type>pom</type>
                              <scope>import</scope>
                          </dependency>
                      </dependencies>
                  </dependencyManagement>
                  <dependencies>
                      <dependency>
                          <groupId>com.fasterxml.jackson.core</groupId>
                          <artifactId>jackson-core</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>com.fasterxml.jackson.core</groupId>
                          <artifactId>jackson-databind</artifactId>
                      </dependency>
                  </dependencies>
              </project>
              """
          )
        );
    }


    @Issue("https://github.com/openrewrite/rewrite/issues/5965")
    @Test
    void upgradeVersionForEjbTypeDependency() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.codeartisans.asadmin", "ejb-example", "0.12", null,
            null, null)),
          pomXml(
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>my-app</artifactId>
               <version>1</version>
               <dependencies>
                <dependency>
              	  <groupId>org.codeartisans.asadmin</groupId>
              	  <artifactId>ejb-example</artifactId>
              	  <version>0.11</version>
              	  <type>ejb</type>
                </dependency>
               </dependencies>
              </project>
              """,
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>my-app</artifactId>
               <version>1</version>
               <dependencies>
                <dependency>
              	  <groupId>org.codeartisans.asadmin</groupId>
              	  <artifactId>ejb-example</artifactId>
              	  <version>0.12</version>
              	  <type>ejb</type>
                </dependency>
               </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/5965")
    @Test
    void upgradeVersionForEjbTypeManagedDependency() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.codeartisans.asadmin", "ejb-example", "0.12", null,
            null, null)),
          pomXml(
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>my-app</artifactId>
               <version>1</version>
               <dependencyManagement>
                <dependencies>
              	  <dependency>
              		  <groupId>org.codeartisans.asadmin</groupId>
              		  <artifactId>ejb-example</artifactId>
              		  <version>0.11</version>
              		  <type>ejb</type>
              	  </dependency>
                </dependencies>
               </dependencyManagement>
              </project>
              """,
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>my-app</artifactId>
               <version>1</version>
               <dependencyManagement>
                <dependencies>
              	  <dependency>
              		  <groupId>org.codeartisans.asadmin</groupId>
              		  <artifactId>ejb-example</artifactId>
              		  <version>0.12</version>
              		  <type>ejb</type>
              	  </dependency>
                </dependencies>
               </dependencyManagement>
              </project>
              """
          )
        );
    }

    @Test
    void shouldNotAddVersionToChildWhenParentDefinesInDependencyManagement() {
        rewriteRun(
          spec -> spec.recipe(new UpgradeDependencyVersion("org.springframework.security", "spring-security-core", "4.2.16.RELEASE", null,
            true, null)),
          pomXml(
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>parent</artifactId>
               <version>1</version>
               <dependencyManagement>
                <dependencies>
              	  <dependency>
              		  <groupId>org.springframework.security</groupId>
              		  <artifactId>spring-security-core</artifactId>
              		  <version>4.2.13.RELEASE</version>
              	  </dependency>
                </dependencies>
               </dependencyManagement>
              </project>
              """,
            """
              <project>
               <groupId>com.mycompany.app</groupId>
               <artifactId>parent</artifactId>
               <version>1</version>
               <dependencyManagement>
                <dependencies>
              	  <dependency>
              		  <groupId>org.springframework.security</groupId>
              		  <artifactId>spring-security-core</artifactId>
              		  <version>4.2.16.RELEASE</version>
              	  </dependency>
                </dependencies>
               </dependencyManagement>
              </project>
              """
          ),
          mavenProject("child",
            pomXml(
              """
                <project>
                	<parent>
                		<groupId>com.mycompany.app</groupId>
                		<artifactId>parent</artifactId>
                		<version>1</version>
                	</parent>
                	<groupId>com.mycompany.app</groupId>
                	<artifactId>child</artifactId>
                	<version>1</version>
                	<dependencies>
                		<dependency>
                			<groupId>org.springframework.security</groupId>
                			<artifactId>spring-security-core</artifactId>
                		</dependency>
                		<dependency>
                			<groupId>org.apache.logging.log4j</groupId>
                			<artifactId>log4j</artifactId>
                			<version>2.13.1</version>
                		</dependency>
                	</dependencies>
                </project>
                """
            )
          )
        );
    }
}
